package leo.peersim.abcast;

import java.util.HashMap;

import org.apache.log4j.Logger;

import peersim.config.Configuration;
import peersim.core.CommonState;
import peersim.core.Control;
import peersim.core.Network;
import peersim.core.Node;

public class ABCastLoadObserver implements Control {
	
private Logger logger = Logger.getLogger(this.getClass().getName());
	
	private final int protocolID;

	private long minExecLatencyAllReplicas = Long.MAX_VALUE;
	private long maxExecLatencyAllReplicas = 0;
	private long avgExecLatencyAllReplicas = 0;
	
	private long minExecLatencyLeaders = Long.MAX_VALUE;
	private long maxExecLatencyLeaders = 0;
	private long avgExecLatencyLeaders = 0;

	private int numOfCompletedTransactions;
	private int numOfUncompletedTransactions;

	private int numOfCompletedClientRequests;
	
	private long minIssuedTime = Long.MAX_VALUE;
	private long maxIssuedTime = 0;
	private long minServedTime = Long.MAX_VALUE;
	private long maxServedTime = 0;
	
	public ABCastLoadObserver(String prefix) {
		this.protocolID = Configuration.getPid(prefix + "." + "protocol");
	}
	
	/**
	 * 
	 */
	@Override
	public boolean execute() { 
		
		logger.info(CommonState.getTime() + "\tLoad Observer...");
		
		this.logProtocolLatencies();
	
		return false;
	}
	
	/**
	 * 
	 * @param protocolID
	 */
	private void logProtocolLatencies() {
		
		for (int i=0; i<Network.size(); i++) {				
			Node n = Network.get(i);
			ABCast abcast = (ABCast) n.getProtocol(this.protocolID);
			HashMap<String,ABCastRound> td = abcast.getManagedTransactions();
			for (String transactionID : td.keySet()) {
				
				ABCastRound t = td.get(transactionID);
				
				if (!t.hasBeenCompleted()) {
					this.numOfUncompletedTransactions++;
					continue;
				}
				this.numOfCompletedTransactions++;
				
				String nodeID = "" + n.getID();
				String nodeIssuer = "" + t.getRequestorNode().getID();
				
				long commandIssuingTime = t.getFSMCommand().getDebug_issuingTime();
				long acceptedTime = t.getDebug_acceptedTime();
				long agreedTime = t.getDebug_agreedTime();
				long executedTime = t.getFSMCommand().get_DebugExecutedTime();
				
				long agreedLatency = agreedTime - commandIssuingTime;
				long executedLatency = executedTime - commandIssuingTime;
				
				long acceptedLatency = -1;
				if (acceptedTime!=-1) {
					acceptedLatency = acceptedTime - commandIssuingTime;
					
					this.minIssuedTime = Math.min(this.minIssuedTime, commandIssuingTime);
					this.maxIssuedTime = Math.max(this.maxIssuedTime, commandIssuingTime);
					
					this.minServedTime = Math.min(this.minServedTime, executedTime);
					this.maxServedTime = Math.max(this.maxServedTime, executedTime);
					
					this.numOfCompletedClientRequests += 1;
					
				}
			}
		}
		
		logger.info("maxIssued: " + this.maxIssuedTime);
		logger.info("minIssued: " + this.minIssuedTime);
		logger.info("maxServed: " + this.maxServedTime);
		logger.info("minServed: " + this.minServedTime);
		logger.info("num of op: " + this.numOfCompletedClientRequests);
		
		long deltaIssued = this.maxIssuedTime-this.minIssuedTime;
		long deltaServed = this.maxServedTime-this.minServedTime;
		
		if (deltaIssued == 0 || deltaServed == 0) {
			logger.debug("deltaIssued: " + deltaIssued + ", deltaServed: " + deltaServed);
			logger.debug("completed requests: " + this.numOfCompletedClientRequests);
		} else {
			long loadGenerated = 1000*this.numOfCompletedClientRequests/deltaIssued;
			long loadServed = 1000*this.numOfCompletedClientRequests/deltaServed;
			logger.info("Load [op/s] generated: " + loadGenerated + ", served: " + loadServed);
		}
	
	}
	
}
